﻿using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Imaging;

using Fortuitus.Core;
using Smrf.NodeXL.Layouts;
using Smrf.NodeXL.Visualization.Wpf;
using Smrf.NodeXL.Core;
using Smrf.NodeXL.Algorithms;
using Microsoft.NodeXL.Algorithms;

namespace ConanLib
{
    public static class NetworkHelper
    {
        //creats a vector and meta-data from line
        public static EditableVector CreateVector(string line, int id)
        {
            string[] array = line.Split("\t".ToCharArray());
            List<double> numbers = new List<double>();

            string name = array[0].Trim();
            string type = string.IsNullOrEmpty(array[1]) ? "N/A" : array[1].Trim();
            string desc = array[2];
            for (int i = 3; i < array.Length; i++)
            {
                numbers.Add(double.Parse(array[i]));
            }
            //Tuple<string, List<double>> ret = new Tuple<string, List<double>>(array[0], vectorA);

            EditableVector v = new EditableVector(numbers);
            v.SetValue<int>("Id", id);
            v.SetValue<string>("Description", desc);
            v.SetValue<string>("Name", name);
            string shortname = name;
            //TODO: Maybe later
            //if (name.Length > 6)
            //{
            //    shortname = name.Substring(0, 3) + "." + name.Substring(name.Length - 2, 2);
            //}
            //v.SetValue<string>("ShortName", shortname);

            v.SetValue<string>("Type", type);

            v.SetMetric("Min", (vector) => { return vector.Where(x => !double.IsNaN(x)).Min(); });
            v.SetMetric("Max", (vector) => { return vector.Where(x => !double.IsNaN(x)).Max(); });
            v.SetMetric("NumberOfNaN", (vector) => { return vector.Count(x => double.IsNaN(x)); }, true);
            return v;
        }

        //reads the standard file format
        public static IOrderedList<EditableVector> ReadData(string filePath, ColorMapper colorMapper, int skip = 1, int take = 0)
        {
            Console.WriteLine("read file {0}", filePath);

            List<string> nameTracker = new List<string>();
            var lines = File.ReadAllLines(filePath);

            int skipLines = Math.Max(skip, 1); //always skip the header 

            OrderedList<EditableVector> features = new OrderedList<EditableVector>();


            lines = lines.Skip(skipLines).ToArray();

            if (take > 0) //take all
                lines = lines.Take(take).ToArray();

            int currentId = 1;
            foreach (var line in lines) //skip the header or more if specified
            {
                var vector = NetworkHelper.CreateVector(line, currentId);
                var name = vector.GetValue<string>("Name");
                if (!nameTracker.Contains(name))
                {
                    features.Add(vector);
                    nameTracker.Add(name);
                    colorMapper.Map(vector.GetValue<string>("Type"));
                    currentId++;
                }
                else
                {
                    var c = Console.ForegroundColor;
                    Console.ForegroundColor = ConsoleColor.Yellow;
                    Console.WriteLine("Warning:");
                    Console.WriteLine("double entry for {0} found. This feature will be skipped", name);
                    Console.ForegroundColor = c;
                }
            }
            return features;
        }

        //filter out feature depending on the filter function
        public static IOrderedList<EditableVector> FilterOut(IOrderedList<EditableVector> features, Func<IVector, bool> filter)
        {
            var ret = new OrderedList<EditableVector>((from x in features
                                                       where filter(x)
                                                       select x));

            return ret;
        }

        //calculate pearson correlations
        public static IEnumerable<ClusterItem> CalculateClusterParallel(IOrderedList<IVector> features, IRelationshipCalculator calc, StreamWriter matrixWriter, double threshold, int minvectorlength = 1)
        {
            long calcsTodo = (((long)features.Count() * (long)features.Count()) - (long)features.Count()) / 2;
            int done = 0;
            object locker = new object();

            Console.WriteLine("calculating {0} correlations, please wait...", calcsTodo);
            List<ClusterItem> res = new List<ClusterItem>();

            //testcode
            //IFeatureRelationCalculator calct = new PearsonFeatureRelationCalc();
            //double pct = calct.CalcRelationship(features[0].Vector, features[1].Vector);

            DateTime start = DateTime.Now;
            Parallel.For(0, features.Length,
                () =>
                {
                    return new List<ClusterItem>();
                },
                (x, loopstate, value) =>
                {
                    int i = (int)x;
                    for (int j = i + 1; j < features.Count(); j++)
                    {
                        if (features[i].GetMetric("NumberOfNaN") == 0 && features[j].GetMetric("NumberOfNaN") == 0)
                        {
                            if (features[i].Length > minvectorlength)
                            {
                                double pc = calc.CalcRelationship(features[i], features[j]);
                                //if (Math.Abs(pc) >= threshold)
                                value.Add(new ClusterItem(features[i], features[j], pc));
                            }
                        }
                        else
                        {

                            var reducedMatrix = NetworkHelper.PairwiseDeletion(features[i], features[j]);
                            if (reducedMatrix.Item1.Length >= minvectorlength)
                            {
                                double pc = calc.CalcRelationship(reducedMatrix.Item1, reducedMatrix.Item2);

                                //if (Math.Abs(pc) >= threshold)
                                value.Add(new ClusterItem(features[i], features[j], pc));
                            }
                        }


                    }

                    return value;
                },
                (final) =>
                {
                    lock (locker)
                    {
                        //SaveCorrelationMatrix(final, matrixPath);
                        foreach (var item in final)
                        {
                            string type1 = item.Feature1.GetValue<string>("Type") == "N/A" ? "" : item.Feature1.GetValue<string>("Type");
                            string type2 = item.Feature2.GetValue<string>("Type") == "N/A" ? "" : item.Feature2.GetValue<string>("Type");
                            matrixWriter.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", item.Feature1.GetValue<string>("Name"), item.Feature2.GetValue<string>("Name"), item.RelationshipValue, type1, type2, item.Feature1.GetValue<string>("Description"), item.Feature2.GetValue<string>("Description"));
                        }
                        matrixWriter.Flush();
                        res.AddRange(final.Where(x => Math.Abs(x.RelationshipValue) >= threshold).AsParallel());
                        done++;
                        if (done % 5000 == 0)
                            Console.WriteLine("{0} sub matrices done...");
                    }
                }
                );
            TimeSpan duration;
            duration = DateTime.Now - start;
            Console.WriteLine("{0} relationships >= {1} found", res.Count(), threshold);
            Console.WriteLine("relationship calclulation time :{0}", duration);
            return res;
        }


        private static void SaveCorrelationMatrix(IEnumerable<ClusterItem> res, string p)
        {
            StreamWriter sw = new StreamWriter(File.OpenWrite(p));

            foreach (var item in res)
            {
                string type1 = item.Feature1.GetValue<string>("Type") == "N/A" ? "" : item.Feature1.GetValue<string>("Type");
                string type2 = item.Feature2.GetValue<string>("Type") == "N/A" ? "" : item.Feature2.GetValue<string>("Type");
                sw.WriteLine("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\t{6}", item.Feature1.GetValue<string>("Name"), item.Feature2.GetValue<string>("Name"), item.RelationshipValue, type1, type2, item.Feature1.GetValue<string>("Description"), item.Feature2.GetValue<string>("Description"));
            }

            sw.Flush();
            sw.Close();
        }

        //cluster independent sub-graphs
        public static List<Cluster> ClusterIndependentSubNetworks(List<Cluster> clusters)
        {
            Console.WriteLine("start clustering of independent networks...");
            DateTime networkBuildStart = DateTime.Now;
            int clusterIndex = 0;
            List<Cluster> ret = new List<Cluster>();
            var currentCluster = clusters[clusterIndex];
            clusters.Remove(currentCluster); //new
            ret.Add(currentCluster);
            var lastJoinedCluster = new Cluster();

            int iteration = 1;
            do //join clusters
            {
                int clusterCountBefore = clusters.Count();
                bool isolatedCluster = true;
                List<Cluster> toJoin = new List<Cluster>();

                // a = DateTime.Now;
                Cluster seedCluster;
                if (lastJoinedCluster.Count == 0)
                    seedCluster = currentCluster; //use the current cluster for search
                else
                    seedCluster = lastJoinedCluster; //just use the last joined cluster for nex search

                toJoin = (from x in clusters.AsParallel()
                          where
                              //x != currentCluster //don't add the current cluster to the result set
                              //&& 
                          seedCluster.HasIntersectionWith(x)
                          select x).ToList();

                lastJoinedCluster = new Cluster(); //create cluster to remember the last joined clusters
                foreach (Cluster cluster in toJoin)
                {
                    lastJoinedCluster.Join(cluster);
                    currentCluster.Join(cluster);
                    cluster.Dropped = true;
                    isolatedCluster = false;
                }
                clusters = (from x in clusters.AsParallel() where x.Dropped == false select x).ToList();

                int clusterCount = clusters.Count();
                if (isolatedCluster)
                {
                    currentCluster = clusters.FirstOrDefault();
                    if (currentCluster != null)
                    {
                        clusters.Remove(currentCluster);
                        ret.Add(currentCluster);
                    }
                }
                Console.WriteLine("Iteration {0} done; {1} cluster joined, {2} left to join", iteration, clusterCountBefore - clusterCount, clusterCount);
                iteration++;
            } while (currentCluster != null);


            //reapply ids for cluster
            int clustenumber = 1;
            foreach (var cluster in ret)
            {
                cluster.Id = clustenumber;
                clustenumber++;
            }

            TimeSpan duration;
            duration = DateTime.Now - networkBuildStart;
            Console.WriteLine("independent network clustering time :{0}", duration);
            return ret;

        }

        //Finds all featues from a graph in a given list of features
        public static List<IVector> GetFeaturesFromGraph(IGraph graph, IEnumerable<IVector> features)
        {
            List<IVector> ret = new List<IVector>();
            foreach (var v in graph.Vertices)
            {
                ret.Add(features.First(x => x.GetValue<string>("Name") == v.Name));
            }
            return ret;
        }

        //Loads a graph from a single cluster
        public static IGraph LoadGraphFromCluster(Cluster cluster, ColorMapper typeColorMapping, double globalPccMin, double globalPccMax)
        {
            return LoadGraphFromClusters(new List<Cluster> { cluster }, typeColorMapping, globalPccMin, globalPccMax, false);
        }

        //Loads a graph from a list of clusters
        public static IGraph LoadGraphFromClusters(IEnumerable<Cluster> clusters, ColorMapper typeColorMapping, double globalPccMin, double globalPccMax, bool grouping)
        {

            IGraph graph = new Graph(GraphDirectedness.Undirected);
            List<GroupInfo> groups = new List<GroupInfo>();

            foreach (var cluster in clusters)
            {

                IVertexCollection oVertices = graph.Vertices;
                IEdgeCollection oEdges = graph.Edges;

                // Create a dictionary to keep track of the vertices that have been
                // added to the graph.  The key is the vertex name and the value is the
                // vertex.

                Dictionary<String, IVertex> oDictionary =
                    new Dictionary<String, IVertex>();
                List<IEdge> egdes = new List<IEdge>();

                GroupInfo group = new GroupInfo();
                foreach (var item in cluster.Items)
                {

                    String sVertex1Name = item.Feature1.GetValue<string>("Name");
                    String sVertex2Name = item.Feature2.GetValue<string>("Name");
                    // Retrieve or create the specified vertices.

                    IVertex oVertex1 =
                        VertexNameToVertex(sVertex1Name, oVertices, oDictionary);

                    IVertex oVertex2 =
                        VertexNameToVertex(sVertex2Name, oVertices, oDictionary);
                    oVertex1.SetValue("Id", item.Feature1.GetValue<int>("Id"));
                    oVertex2.SetValue("Id", item.Feature2.GetValue<int>("Id"));

                    oVertex1.SetValue("Type", item.Feature1.GetValue<string>("Type"));
                    oVertex2.SetValue("Type", item.Feature2.GetValue<string>("Type"));

                    oVertex1.SetValue("Description", item.Feature1.GetValue<string>("Description"));
                    oVertex2.SetValue("Description", item.Feature2.GetValue<string>("Description"));
                    System.Drawing.ColorConverter colorConverter = new System.Drawing.ColorConverter();
                    if (typeColorMapping != null)
                        oVertex1.SetValue(ReservedMetadataKeys.PerColor, typeColorMapping.Map(oVertex1.GetValue("Type").ToString()));
                    if (typeColorMapping != null)
                        oVertex2.SetValue(ReservedMetadataKeys.PerColor, typeColorMapping.Map(oVertex2.GetValue("Type").ToString()));
                    oVertex1.SetValue(ReservedMetadataKeys.PerVertexLabel, sVertex1Name);
                    oVertex1.SetValue(ReservedMetadataKeys.PerVertexLabelFillColor, System.Windows.Media.Colors.Black);
                    oVertex2.SetValue(ReservedMetadataKeys.PerVertexLabel, sVertex2Name);
                    oVertex2.SetValue(ReservedMetadataKeys.PerVertexLabelFillColor, System.Windows.Media.Colors.Transparent);

                    // Add an edge connecting the vertices.
                    IEdge e = oEdges.Add(oVertex1, oVertex2, false);
                    group.Vertices.AddLast(oVertex1);
                    group.Vertices.AddLast(oVertex2);


                    egdes.Add(e);
                    e.Name = sVertex1Name + "_&&_" + sVertex2Name;
                    // Single weight = Single.Parse(item.RelationshipValue);
                    e.SetValue("Weight", item.RelationshipValue);

                    //
                    double alpha = LinearScale(Math.Abs((double)e.GetValue("Weight")), globalPccMin, globalPccMax, 50, 255);

                    //TODO: doesn't work very well, will come back to this later
                    //double edgeweight = LinearScale(Math.Abs((double)e.GetValue("Weight")), globalPccMin, globalPccMax, 1, 100);
                    //e.SetValue(ReservedMetadataKeys.EdgeWeight, edgeweight);


                    e.SetValue(ReservedMetadataKeys.PerAlpha, (Single)alpha);
                    if ((double)e.GetValue("Weight") < 0)
                    {
                        e.SetValue(ReservedMetadataKeys.PerEdgeStyle, EdgeStyle.DashDotDot);

                    }
                    else
                    {
                        e.SetValue(ReservedMetadataKeys.PerEdgeStyle, EdgeStyle.Solid);
                    }
                }
                groups.Add(group);
                oDictionary.Clear();
            }

            if (grouping)
            {
                graph.SetValue(ReservedMetadataKeys.GroupInfo, groups.ToArray());
            }
            if (clusters.Count() > 1)
            {
                graph.SetValue("ClusterId", 0);
            }
            else
            {
                graph.SetValue("ClusterId", clusters.First().Id);
            }
            return (graph);
        }

        private static IVertex VertexNameToVertex(String sVertexName, IVertexCollection oVertices, Dictionary<String, IVertex> oDictionary)
        {

            IVertex oVertex;

            if (!oDictionary.TryGetValue(sVertexName, out oVertex))
            {
                oVertex = oVertices.Add();
                oVertex.Name = sVertexName;
                oDictionary.Add(sVertexName, oVertex);
            }

            return (oVertex);
        }

        public static List<IGraph> ClusterSubNetworks(List<IGraph> independentSubgraphs, ColorMapper _colorMapper, double _minPCC, double _maxPCC)
        {
            var exceptions = new ConcurrentQueue<Exception>();
            object locker = new object();
            List<IGraph> res = new List<IGraph>();

            ClusterCalculator cc = new ClusterCalculator();
            Console.WriteLine("Clustering subnetworks using WakitaTsurumi algorithm... ");
            List<string> _graphFilesNew = new List<string>();
            for (int idx = 0; idx < independentSubgraphs.Count; idx++)
            {
                //try
                //{
                //   var val = new List<IGraph>();
                List<GroupInfo> groups = new List<GroupInfo>();
                var graph = independentSubgraphs[idx];

                cc.Algorithm = ClusterAlgorithm.WakitaTsurumi;
                var cl = cc.CalculateGraphMetrics(graph);
                var vertLookup = graph.Vertices.ToDictionary(x => x.Name);
                var edgeLookup = graph.Edges.ToDictionary(x => x.Name);
                if (cl.Count > 1) //don't output the trivial case where the subgraph is the same as the full graph
                {
                    foreach (var community in cl)
                    {
                        GroupInfo gi = new GroupInfo();
                        var subGraph = MySubgraphCalculator.GetSubgraphAsNewGraph(community.Vertices);
                        // var edges = subGraph.Edges.Where(x => x != null);
                        var edges = subGraph.Edges;
                        foreach (var v in subGraph.Vertices)
                        {
                            v.SetValue("Type", vertLookup[v.Name].GetValue("Type"));
                            v.SetValue("Description", vertLookup[v.Name].GetValue("Description"));
                            v.SetValue(ReservedMetadataKeys.PerColor, _colorMapper.Mapping[vertLookup[v.Name].GetValue("Type").ToString()]);
                            v.SetValue(ReservedMetadataKeys.PerVertexLabel, v.Name);
                            v.SetValue(ReservedMetadataKeys.PerVertexLabelFillColor, System.Windows.Media.Colors.Transparent);
                            gi.Vertices.AddLast(vertLookup[v.Name]);

                        }
                        foreach (var e in edges)
                        {
                            double weight = (double)edgeLookup[e.Name].GetValue("Weight");
                            e.SetValue("Weight", weight);
                            e.SetValue(ReservedMetadataKeys.EdgeWeight, Math.Abs(weight));
                            double alpha = NetworkHelper.LinearScale(Math.Abs(weight), _minPCC, _maxPCC, 50, 255);
                            e.SetValue(ReservedMetadataKeys.PerAlpha, (Single)alpha);
                            if (weight < 0)
                            {
                                e.SetValue(ReservedMetadataKeys.PerEdgeStyle, EdgeStyle.DashDotDot);
                            }
                            else
                            {
                                e.SetValue(ReservedMetadataKeys.PerEdgeStyle, EdgeStyle.Solid);
                            }
                            //   e.SetValue(ReservedMetadataKeys.EdgeWeight, Math.Pow((Math.Exp(Math.Abs(weight))), 4));
                        }
                        subGraph.SetValue("ParentClusterId", (int?)graph.GetValue("ClusterId"));
                        res.Add(subGraph);
                        groups.Add(gi);

                    }
                }
                graph.SetValue(ReservedMetadataKeys.GroupInfo, groups.ToArray());
            }


            if (exceptions.Count > 0) throw new AggregateException(exceptions);


            Console.WriteLine("Clustering subnetworks done");
            return res;
        }


        //public static List<IGraph> ClusterSubNetworks(List<IGraph> independentSubgraphs, ColorMapping _colorMapper, double _minPCC, double _maxPCC)
        //{
        //    var exceptions = new ConcurrentQueue<Exception>();
        //    object locker = new object();
        //    List<IGraph> res = new List<IGraph>();

        //    ClusterCalculator cc = new ClusterCalculator();
        //    Console.WriteLine("Clustering subnetworks using WakitaTsurumi algorithm... ");
        //    List<string> _graphFilesNew = new List<string>();
        //    Parallel.For(0, independentSubgraphs.Count,
        //        () => { return new List<IGraph>(); }
        //        , (idx, loopstate, val) =>
        //    {
        //        //try
        //        //{
        //            var graph = independentSubgraphs[idx];
        //            cc.Algorithm = ClusterAlgorithm.WakitaTsurumi;
        //            var cl = cc.CalculateGraphMetrics(graph);
        //            var vertLookup = graph.Vertices.ToDictionary(x => x.Name);
        //            var edgeLookup = graph.Edges.ToDictionary(x => x.Name);
        //            if (cl.Count > 1) //don't output the trivial case where the subgraph is the same as the full graph
        //            {
        //                foreach (var community in cl)
        //                {
        //                    var subGraph = MySubgraphCalculator.GetSubgraphAsNewGraph(community.Vertices);
        //                    // var edges = subGraph.Edges.Where(x => x != null);
        //                    var edges = subGraph.Edges;
        //                    foreach (var v in subGraph.Vertices)
        //                    {
        //                        v.SetValue("Type", vertLookup[v.Name].GetValue("Type"));
        //                        v.SetValue("Description", vertLookup[v.Name].GetValue("Description"));
        //                        v.SetValue(ReservedMetadataKeys.PerColor, _colorMapper.Mapping[vertLookup[v.Name].GetValue("Type").ToString()]);
        //                        v.SetValue(ReservedMetadataKeys.PerVertexLabel, v.Name);

        //                    }
        //                    foreach (var e in edges)
        //                    {

        //                        double weight = (double)edgeLookup[e.Name].GetValue("Weight");
        //                        e.SetValue("Weight", weight);
        //                        double alpha = NetworkHelper.LinearScale(weight, _minPCC, _maxPCC);
        //                        e.SetValue(ReservedMetadataKeys.PerAlpha, (Single)alpha);
        //                    }
        //                    subGraph.SetValue("ParentClusterId", (int?)graph.GetValue("ClusterId"));
        //                    val.Add(subGraph);
        //                }
        //            }
        //            return val;
        //        //}
        //        //catch (Exception ex)
        //        //{
        //        //  //  Console.WriteLine(ex.StackTrace);
        //        //    exceptions.Enqueue(ex);

        //        //    return new List<IGraph>();
        //        //}
        //    }, final =>
        //        {
        //            lock (locker)
        //            {
        //                res.AddRange(final);

        //            }
        //        }

        //    );

        //    if (exceptions.Count > 0) throw new AggregateException(exceptions);


        //    Console.WriteLine("Clustering subnetworks done");
        //    return res;
        //}

        //genrate picture 


        //generate HeatMap file
        public static void GenerateHeatMap(IList<IVector> features, string filePath, int maxRows = int.MaxValue)
        {
            List<IVector> maxedFeatures = features.Take(maxRows).ToList();
            DummyForm b = new DummyForm();
            Bitmap bmp = new Bitmap(100, 100);
            Graphics g = Graphics.FromImage(bmp);
            //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;

            Font f = new Font("Verdana", 9);

            float maxIdWidth = 0;
            float maxIdHeight = 10;
            foreach (var item in maxedFeatures)
            {
                maxIdWidth = Math.Max(maxIdWidth, g.MeasureString(item.GetValue<string>("Name"), f).Width);
                maxIdHeight = Math.Max(maxIdHeight, g.MeasureString(item.GetValue<string>("Name"), f).Height);
            }

            float sqareLength = Math.Max(maxIdHeight, 10f);

            float width = maxIdWidth + (sqareLength * maxedFeatures.First().Length);
            float height = sqareLength * maxedFeatures.Count();


            bmp = new Bitmap((int)Math.Ceiling(width), (int)Math.Ceiling(height));
            g = Graphics.FromImage(bmp);
            //g.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.AntiAlias;
            g.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias;

            //    MinMaxNorm mmn = new MinMaxNorm();

            //  g.FillRectangle(new SolidBrush(System.Drawing.Color.Black), 0, 0, width, height);


            double min = double.MaxValue;
            double max = double.MinValue;
            //foreach (var feat in features)
            //{
            //   // var normMinMax = mmn.Transform(feat.Vector);
            //    min = Math.Min(min, feat.Vector.Where(x => !double.IsNaN(x)).Min());
            //    max = Math.Max(max, feat.Vector.Where(x => !double.IsNaN(x)).Max());
            //}


            for (int i = 0; i < maxedFeatures.Count; i++)
            {
                float y = i * sqareLength;
                float x = 0;
                g.DrawString(maxedFeatures[i].GetValue<string>("Name"), f, System.Drawing.Brushes.Black, x, y);
                min = maxedFeatures[i].GetMetric("Min");// Math.Min(min, features[i].Vector.Where(z => !double.IsNaN(z)).Min());
                max = maxedFeatures[i].GetMetric("Max"); //Math.Max(max, features[i].Vector.Where(z => !double.IsNaN(z)).Max());
                for (int j = 0; j < maxedFeatures[i].Length; j++)
                {

                    System.Drawing.Color heat;
                    x = maxIdWidth + (j * sqareLength);

                    var val = maxedFeatures[i][j];
                    if (val < 0)
                        heat = HeatMapper.MapValue(maxedFeatures[i][j], min, 0, System.Drawing.Color.Blue, System.Drawing.Color.Black);
                    else
                        heat = HeatMapper.MapValue(maxedFeatures[i][j], 0, max, System.Drawing.Color.Black, System.Drawing.Color.Yellow);

                    g.FillRectangle(new SolidBrush(heat), x, y, sqareLength, sqareLength);


                }
            }

            bmp.Save(filePath, System.Drawing.Imaging.ImageFormat.Png);
            bmp.Dispose();


        }

        //scales a weight value to alpha value between 50 - 255
        public static double LinearScale(double valX, double minX, double maxX, double minY, double maxY)
        {
            if (minX == maxX)
                return maxY;

            double scaleXMin = 0;
            double scaleXMax = maxX - minX;

            double scaleYMin = 0;
            double scaleYMax = maxY - minY;

            double x = valX - minX;
            double scaleFactor = scaleYMax / scaleXMax;

            //double scaleMin = 50.0F;
            //double scaleMax = 255F;
            double y = ((x * scaleFactor) + minY);
            if (y > maxY)
            {
                //System.Diagnostics.Debug.Assert(y <= minY, "y should be smaller or equal to from");
                //var temp = Console.ForegroundColor;
                //Console.ForegroundColor = ConsoleColor.Red;
                //Console.WriteLine("alpha bigger than 255. alpha={0}", ret);
                //Console.WriteLine("val={0}, min={1}, max={2}", ret, min, max);
                y = maxY;
                // Console.ForegroundColor = temp;
            }
            if (y < minY)
            {
                //System.Diagnostics.Debug.Assert(y >= minY, "y should be bigger or equal to from");
                //var temp = Console.ForegroundColor;
                //Console.ForegroundColor = ConsoleColor.Red;
                //Console.WriteLine("alpha smaller than 0. alpha={0}", ret);
                //Console.WriteLine("val={0}, min={1}, max={2}", ret, min, max);
                y = minY;
                //Console.ForegroundColor = temp;
            }
            return y;
        }

        public static Tuple<List<double>, List<double>> RemoveMissingValues(List<double> a, List<double> b)
        {
            Tuple<List<double>, List<double>> ret = new Tuple<List<double>, List<double>>(new List<double>(), new List<double>());
            for (int i = 0; i < a.Count; i++)
            {
                if (!double.IsNaN(a[i]) && !double.IsNaN(b[i]))
                {
                    ret.Item1.Add(a[i]);
                    ret.Item2.Add(b[i]);
                }
            }

            return ret;
        }


        public static Tuple<IVector, IVector> PairwiseDeletion(IVector a, IVector b)
        {
            Tuple<IVector, IVector> ret; // new Tuple<List<double>, List<double>>(new List<double>(), new List<double>());
            List<int> indxMask = new List<int>();
            int minLength = Math.Min(a.Length, b.Length);

            for (int i = 0; i < minLength; i++)
            {
                if (!double.IsNaN(a[i]) && !double.IsNaN(b[i]))
                {
                    indxMask.Add(i);
                }
            }
            ret = new Tuple<IVector, IVector>(new MaskedVectorView(a, indxMask.ToArray()), new MaskedVectorView(b, indxMask.ToArray()));
            return ret;
        }


        public static void GenerateGraphImage(IGraph subGraph, string filePath, int targetDPI, LayoutAlgorithm layoutAlg)
        {
            Int32 GraphWidth = 1024;
            Int32 GraphHeight = 1024;

            Double SCREEN_DPI = 96.0;
            Double TARGET_DPI = targetDPI;


            ILayout layout = new FruchtermanReingoldLayout();
            switch (layoutAlg)
            {
                case LayoutAlgorithm.CIRCLE:
                    layout = new CircleLayout(); break;
                case LayoutAlgorithm.FMR:
                    layout = new FruchtermanReingoldLayout(); break;
                case LayoutAlgorithm.GRID:
                    layout = new GridLayout(); break;
                case LayoutAlgorithm.HKFM:
                    layout = new HarelKorenFastMultiscaleLayout(); break;
                case LayoutAlgorithm.RAND:
                    layout = new RandomLayout(); break;
                case LayoutAlgorithm.POLAR:
                    layout = new PolarLayout(); break;
                case LayoutAlgorithm.SINUS:
                    layout = new SinusoidLayout(true, 5); break;
                case LayoutAlgorithm.SPIRAL:
                    layout = new SpiralLayout(); break;
                case LayoutAlgorithm.SUGI:
                    layout = new SugiyamaLayout(); break;
                default:
                    break;
            }
            layout.Margin = 20;
            // layout.ImproveLayoutOfGroups = true;

            layout.LayoutStyle = LayoutStyle.UseGroups;

            LayoutContext layoutContext = new LayoutContext(
            new System.Drawing.Rectangle(0, 0, GraphWidth, GraphHeight));

            NodeXLVisual nodeXLVisual = new NodeXLVisual();

            layout.LayOutGraph(subGraph, layoutContext);
            GraphDrawingContext oGraphDrawingContext = new GraphDrawingContext(
                                   new Rect(0, 0, GraphWidth, GraphHeight), layout.Margin,
                                   System.Windows.Media.Color.FromRgb(255, 255, 255));

            nodeXLVisual.GraphDrawer.EdgeDrawer.CurveStyle = EdgeCurveStyle.Bezier;
            nodeXLVisual.GraphDrawer.DrawGraph(subGraph, oGraphDrawingContext);
            // RenderTargetBitmap oRenderTargetBitmap = new RenderTargetBitmap(
            //GraphWidth, GraphHeight, 96, 96, PixelFormats.Default);
            RenderTargetBitmap oRenderTargetBitmap = new RenderTargetBitmap(
                (int)Math.Floor(GraphWidth * (TARGET_DPI / SCREEN_DPI))
                , (int)Math.Floor(GraphHeight * (TARGET_DPI / SCREEN_DPI))
                , TARGET_DPI
                , TARGET_DPI
                , PixelFormats.Default);


            oRenderTargetBitmap.Render(nodeXLVisual);
            BmpBitmapEncoder oBmpBitmapEncoder = new BmpBitmapEncoder();
            oBmpBitmapEncoder.Frames.Add(BitmapFrame.Create(oRenderTargetBitmap));
            MemoryStream oMemoryStream = new MemoryStream();
            oBmpBitmapEncoder.Save(oMemoryStream);
            Bitmap oBitmap = new Bitmap(oMemoryStream);

            FileInfo fi = new FileInfo(filePath);
            string fileName = fi.FullName.Replace(fi.Extension, ".png");
            FileStream fs = File.Create(fileName);
            oBitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);

            fs.Flush();
            fs.Close();
        }
        public static void GenerateGraphImageAsync(IGraph subGraph, string filePath, int targetDPI, LayoutAlgorithm layoutAlg)
        {
            Int32 GraphWidth = 1920;
            Int32 GraphHeight = 1080;

            Double SCREEN_DPI = 96.0;
            Double TARGET_DPI = targetDPI;

            Boolean graphDrawn = false;

            ILayout layout = new FruchtermanReingoldLayout();
            switch (layoutAlg)
            {
                case LayoutAlgorithm.CIRCLE:
                    layout = new CircleLayout(); break;
                case LayoutAlgorithm.FMR:
                    layout = new FruchtermanReingoldLayout(); break;
                case LayoutAlgorithm.GRID:
                    layout = new GridLayout(); break;
                case LayoutAlgorithm.HKFM:
                    layout = new HarelKorenFastMultiscaleLayout(); break;
                case LayoutAlgorithm.RAND:
                    layout = new RandomLayout(); break;
                case LayoutAlgorithm.POLAR:
                    layout = new FruchtermanReingoldLayout(); break;
                case LayoutAlgorithm.SINUS:
                    layout = new SinusoidLayout(true, 5); break;
                case LayoutAlgorithm.SPIRAL:
                    layout = new SpiralLayout(); break;
                case LayoutAlgorithm.SUGI:
                    layout = new SugiyamaLayout(); break;
                default:
                    break;
            }
            layout.Margin = 20;
            // layout.ImproveLayoutOfGroups = true;

            layout.LayoutStyle = LayoutStyle.UseGroups;

            LayoutContext layoutContext = new LayoutContext(
            new System.Drawing.Rectangle(0, 0, GraphWidth, GraphHeight));

            NodeXLVisual nodeXLVisual = new NodeXLVisual();


            // Awful hack: Use a delegate to complete the task after the graph has
            // been asynchronously laid out.

            layout.LayOutGraphCompleted += delegate
            {
                GraphDrawingContext oGraphDrawingContext = new GraphDrawingContext(
                                       new Rect(0, 0, GraphWidth, GraphHeight), layout.Margin,
                                       System.Windows.Media.Color.FromRgb(255, 255, 255));

                nodeXLVisual.GraphDrawer.EdgeDrawer.CurveStyle = EdgeCurveStyle.Bezier;
                nodeXLVisual.GraphDrawer.DrawGraph(subGraph, oGraphDrawingContext);
                // RenderTargetBitmap oRenderTargetBitmap = new RenderTargetBitmap(
                //GraphWidth, GraphHeight, 96, 96, PixelFormats.Default);
                RenderTargetBitmap oRenderTargetBitmap = new RenderTargetBitmap(
                    (int)Math.Floor(GraphWidth * (TARGET_DPI / SCREEN_DPI))
                    , (int)Math.Floor(GraphHeight * (TARGET_DPI / SCREEN_DPI))
                    , TARGET_DPI
                    , TARGET_DPI
                    , PixelFormats.Default);


                oRenderTargetBitmap.Render(nodeXLVisual);
                BmpBitmapEncoder oBmpBitmapEncoder = new BmpBitmapEncoder();
                oBmpBitmapEncoder.Frames.Add(BitmapFrame.Create(oRenderTargetBitmap));
                MemoryStream oMemoryStream = new MemoryStream();
                oBmpBitmapEncoder.Save(oMemoryStream);
                Bitmap oBitmap = new Bitmap(oMemoryStream);

                FileInfo fi = new FileInfo(filePath);
                string fileName = fi.FullName.Replace(fi.Extension, ".png");
                FileStream fs = File.Create(fileName);
                oBitmap.Save(fs, System.Drawing.Imaging.ImageFormat.Png);

                fs.Flush();
                fs.Close();

                graphDrawn = true;
            };

            layout.LayOutGraphAsync(subGraph, layoutContext);

            while (!graphDrawn)
            {
                Application.DoEvents();
                System.Threading.Thread.Sleep(100);
            }
        }



        private class DummyForm : Form
        {

            public DummyForm()
            {

            }
        }


    }


}
